
//线程头文件
#include <pthread.h>
//gtk头文件
#include <gtk/gtk.h>
//时间获取头文件
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>


/******************linklist.h************************************/ 
//---------------------全局变量区------------------------------
//创建一个数据结构体
typedef struct {
	int time;
	char lrc[128];
} MSG;

//定义一个结点结构体
typedef struct node {
//数据域，可以只有一个，也可以有多个
	MSG data;

//指针域
//指针域：用于保存下一个结点的地址，下一个结点的地址还是当前的结构体类型
//另外保存下一个结点的地址后，还需要操作结点，所以综上指针域应该为结构体指针类型
	struct node *next;

} LinkList;

//---------------------函数声明区------------------------------
//创建一个空的单链表
LinkList *LinkListCreate();
//遍历链表
void LinkListShow(LinkList* h);
//尾插法插入数据
void LinkListTailInsert(LinkList* h,int time,char* lrc);
//根据时间去寻找歌词
LinkList* FindLrcByTime(LinkList* h,int time);
//链表的释放
LinkList* FreeLinkList(LinkList* h);


/******************lrc.h************************************/ 
char *file_data;
LinkList* h;
//歌词处理函数
void LrcDeal();
//获取歌词
void GetLrc(const char* file_path);

/******************gtk.h************************************/ 
GtkButton *btnPlay;        //播放键
GtkButton *btnPause;       //暂停键
int playFlag = 1;          //播放标志量
void quit();               //退出函数

void playClick();          //播放键绑定事件
void pauseClick();         //暂停键绑定事件
void switchNext();         //下一首按键绑定事件
void switchPre();          //上一首按键绑定事件

void enter_callback(GtkWidget* entry);
void mplayer_quit();
void start();
void gtk_design(); 

////文件描述符
int fd;
//无名管道有两个文件描述符，一个负责收，一个负责发
int pipe_fd[2];
GtkWidget* progress;
GtkWidget* sec_label;
GtkWidget* fis_label;
GtkWidget* thr_label;
GtkWidget* search_resualt;
char last_lrc[128]="";
char cur_lrc[128]="";
char next_lrc[128]="";


//接收命令线程
void *Recv_cmd(void *arg) {
	int progress_pos=0;
	int time = 0;
	LinkList* cur_node;
//从终端上接收命令（无名管道）
	while(1) {
		char buf[128]="";
		read(pipe_fd[0],buf,sizeof(buf));
//根据接收的内容进行一个甄别
//对进度条命令的接收
		if (strncmp(buf,"ANS_PERCENT_POSITION=",strlen("ANS_PERCENT_POSITION="))==0) {
//sscnaf从字符串当中获取内容
			sscanf(buf,"ANS_PERCENT_POSITION=%d",&progress_pos);
//给进度条设值
			gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress),\
			                              (float)progress_pos/100.0);
		} else if (strncmp(buf,"ANS_TIME_POSITION=",strlen("ANS_TIME_POSITION="))==0) {
//sscnaf从字符串当中获取内容
			sscanf(buf,"ANS_TIME_POSITION=%d",&time);
			//printf("cur_time  %d",time);

//将对应的歌词放到标签上
			cur_node = FindLrcByTime(h,time);
//获取歌词下进行判断
			if (cur_node != NULL) {
				//当歌词播放至末尾时会产生一个段错误
				//防止歌词播放至末尾崩溃
				if(cur_node->next == NULL) {
					continue;
				}
//将当前的歌词拷贝到上一句歌词中
				strcpy(last_lrc,cur_lrc);
				strcpy(cur_lrc,cur_node->data.lrc);
				strcpy(next_lrc,cur_node->next->data.lrc);
			}
			gtk_label_set_text(GTK_LABEL(fis_label),last_lrc);
			gtk_label_set_text(GTK_LABEL(sec_label),cur_lrc);
			gtk_label_set_text(GTK_LABEL(thr_label),next_lrc);
		}
	}
}
//发送命令线程
void *Send_cmd(void *arg) {
//往有名管道当中去发
	while(1) {
		
		write(fd,"get_percent_pos\n",strlen("get_percent_pos\n"));
		write(fd,"get_time_pos\n",strlen("get_time_pos\n"));
		sleep(1);
	}
}



/******************歌曲.h************************************/ 
char i=1;
int Song_size = 3;
char* Song_mp3[4]= {"./music/xihuanni.mp3","./music/hello.mp3","./music/paomo.mp3"};
char* Song_lrc[4]= {"./lrc/xihuanni.lrc","./lrc/hello.lrc","./lrc/paomo.lrc"};
char* Song_name[4]= {"xihuanni","hello","paomo"};


int main(int argc, char  *argv[]) {

		gtk_init(&argc,&argv);
//创建无名管道
	pipe(pipe_fd);
//有名管道--在有血缘关系的进程间进行通信
	mkfifo("cmd",0777);
//创建进程
	pid_t pid;
	pid = fork();
//父进程处理页面相关内容
	if (pid > 0 ) {
		fd = open("./cmd",O_WRONLY);
		gtk_design();
		gtk_main();
	}
//子进程打开音乐播放器
	else if (pid == 0) {
//重定向无名管道文件 将无名管道的一端=标准输出
		dup2(pipe_fd[1],1);
		   //为Mplayer建立播放列表
    	//system("ls ./music/*mp3 > music.lst");
 		//popen("mplayer -slave -quiet -input file=cmd -loop 0 -playlist music.lst &", "r");
		start(Song_mp3[i]);
		
	} else {
		perror("pid");
	}
	return 0;
}

void mplayer_quit() {
	
	system("killall -9 mplayer");
	h = FreeLinkList(h);
	
	gtk_main_quit;
	write(fd,"quit\n",strlen("quit\n"));
	close(fd);
	system("killall demo");
}

void start(char *path){

	system("killall mplayer");
	gtk_label_set_text(GTK_LABEL(search_resualt)," ");
	pid_t pid;  //pid_t 是一个整数类型，用于表示进程的ID
	pid= fork();
	if (pid>0) {
		GetLrc(Song_lrc[i]);
	} else if(pid == 0) { //子进程
		dup2(pipe_fd[1],1);
		//启动mplayer
		execlp("mplayer", "mplayer", "-slave","-quiet",\
		       "-idle","-input", "file=./cmd",path,NULL);
	}else {
		perror("fork");
	}
	// GetLrc(Song_lrc[i]);
	// execlp("mplayer", "mplayer", "-slave","-quiet",\
	//  	       "-idle","-input", "file=./cmd",path,NULL);
}
void playClick()
{
	 fd = open("./cmd",O_WRONLY);
    gtk_widget_show(btnPause);
 	gtk_widget_hide(btnPlay);
 
	write(fd,"pause\n",strlen("pause\n"));
	printf("pppause");    
  
}

void pauseClick()
{
 
    gtk_widget_show(btnPlay);
	gtk_widget_hide(btnPause);

	write(fd,"pause\n",strlen("pause\n"));
	close(fd);
	printf("ppplay");
	
}

void switchPre(){
	i--;
      system("killall  mplayer");
  
  if(i < 0) i = Song_size-1;
  	start(Song_mp3[i]);
fd = open("./cmd",O_WRONLY);
}

void switchNext(){  
	i++;
      system("killall  mplayer");
  if(i == Song_size ) i = 0;
  
	start(Song_mp3[i]);
fd = open("./cmd",O_WRONLY);
}


void enter_callback(GtkWidget *entry){
	const gchar *entry_text;
// 获得文本内容
	entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
	printf("Entry contents: %s\n", entry_text);
	//搜索
	int index = 0;
	for(int index = 0; index < Song_size; index++){
		if(strstr(Song_name[index],entry_text)){
			i = index;
			start(Song_mp3[i]);
			gtk_label_set_text(GTK_LABEL(search_resualt),"successful");
			return;
		}
	}
			gtk_label_set_text(GTK_LABEL(search_resualt),"failed");	
			return;	

}

void gtk_design() {
	
		gtk_init(NULL,NULL);

		GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

		gtk_widget_set_size_request(window,800,480);

//创建一个固定布局
		GtkWidget* fixed = gtk_fixed_new();
		gtk_container_add(GTK_CONTAINER(window),fixed);
//创建进度条
		progress= gtk_progress_bar_new();
//设置进度条的方向
		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress),\
		                                 0);

//设置进度条大小
		gtk_widget_set_size_request(progress,600,10);
		gtk_fixed_put(GTK_FIXED(fixed),progress,100,410);

//铺设Mplayer的Logo
    const GdkPixbuf *src_pixbuf = gdk_pixbuf_new_from_file("./logo/logo.png", NULL);
    GdkPixbuf *dest = gdk_pixbuf_scale_simple(src_pixbuf, 200, 170, GDK_INTERP_BILINEAR);
    GtkWidget *img = gtk_image_new_from_pixbuf(dest);
    gtk_fixed_put(fixed, img, 30, 30);
    gtk_widget_show(img);

//创建一个标签歌词
		fis_label =gtk_label_new("");
		gtk_fixed_put(GTK_FIXED(fixed),fis_label,400,115);

		sec_label =gtk_label_new("");
		gtk_fixed_put(GTK_FIXED(fixed),sec_label,400,215);

		thr_label =gtk_label_new("");
		gtk_fixed_put(GTK_FIXED(fixed),thr_label,400,315);

		search_resualt =gtk_label_new("");
		gtk_fixed_put(GTK_FIXED(fixed),search_resualt,50,400);

		gtk_widget_show_all(window);

		g_signal_connect(window,"destroy",G_CALLBACK(mplayer_quit),NULL);

		GetLrc(Song_lrc[i]);
		
		
		    //暂停播放按钮 ||
    GtkWidget *pauseIcon = gtk_image_new();
    gtk_image_set_from_stock(pauseIcon, GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_LARGE_TOOLBAR);
    
    btnPause = gtk_button_new();
    gtk_button_set_image(btnPause, pauseIcon);
    gtk_fixed_put(fixed, btnPause, 120, 300);
    g_signal_connect(btnPause, "clicked", G_CALLBACK(pauseClick), NULL);
    gtk_widget_show(btnPause);
        
    //开始播放按钮
    GtkWidget *playIcon = gtk_image_new();
    gtk_image_set_from_stock(playIcon, GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_LARGE_TOOLBAR);
    
    btnPlay = gtk_button_new();
    gtk_button_set_image(btnPlay, playIcon);
	  gtk_fixed_put(fixed, btnPlay, 120, 300);
    g_signal_connect(btnPlay, "clicked", G_CALLBACK(playClick), NULL);
    
        //切换到上一首按钮
    GtkImage *pre = gtk_image_new();
    gtk_image_set_from_stock(pre, GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_LARGE_TOOLBAR);

    GtkButton *btnPre = gtk_button_new();
    gtk_button_set_image(btnPre, pre);
    gtk_fixed_put(fixed, btnPre, 80, 300);
    g_signal_connect(btnPre, "clicked", G_CALLBACK(switchPre), NULL);
    gtk_widget_show(btnPre);

    //切换到下一首按钮
    GtkImage *next = gtk_image_new();
    gtk_image_set_from_stock(next, GTK_STOCK_MEDIA_NEXT,GTK_ICON_SIZE_LARGE_TOOLBAR);

    GtkButton *btnNext = gtk_button_new();
    gtk_button_set_image(btnNext, next);
    gtk_fixed_put(fixed, btnNext, 160, 300);
    g_signal_connect(btnNext, "clicked", G_CALLBACK(switchNext), NULL);
    gtk_widget_show(btnNext);
    
    // 创建行编辑
    GtkWidget *entry = gtk_entry_new(); 
    gtk_fixed_put(fixed, entry, 50, 350);
gtk_entry_set_max_length(GTK_ENTRY(entry), 100); // 设置行编辑显示最大字符的长度
gtk_entry_set_text(GTK_ENTRY(entry), "hello word"); // 设置内容
gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE); // 正常模式
    g_signal_connect(entry, "activate", G_CALLBACK(enter_callback), entry);
    gtk_widget_show(entry);
//创建两个线程 一个线程负责一直发命令，另一个进行负责一直收命令
		pthread_t send_th,recv_th;

		pthread_create(&send_th,NULL,Send_cmd,NULL);
		pthread_create(&recv_th,NULL,Recv_cmd,NULL);
		
		
	gdk_threads_enter();	// 进入多线程互斥区域
	gtk_main();
	gdk_threads_leave();    // 退出多线程互斥区域		
}





//歌词处理函数
void LrcDeal() {
//创建一个空链表
	h = LinkListCreate();
//定义两个char*的变量用于处理时间和歌词
	char* str_time=NULL;
	char* str_lrc =NULL;
	int time =0 ;
//切割歌词
//需要先切割一次
	char *ret = strtok(file_data,"\r\n");
	LinkListTailInsert(h,0,ret);
	while(ret != NULL) {
		ret = strtok(NULL,"\r\n");
		if (ret == NULL) {
			break;
		}

//对每一行歌词进行处理
//a.将时间处理出来
		str_time = ret;
		if (*str_time == '[') {
			int m=0,s=0;
//将括号中的分钟和秒钟取出
			sscanf(str_time,"[%d:%d.64]",&m,&s);

//要将其转化为秒钟为单位
			time = m*60+s;
//printf("time = %d\n",time);
		}

//b.解析歌词
		str_lrc =ret;
		if (*str_lrc == '[') {
//strncmp 比较两个字符前n个字符是否一致
			if (strncmp(str_lrc,"[0",strlen("[0"))!=0) {
//非歌词内容信息
				printf("lrc: %s\n",str_lrc);
			} else {
//字符指针往后移动十位
				str_lrc = str_lrc+10;
				printf("lrc: %s\n",str_lrc);
			}
		}
//将时间和歌词插入到链表当中
//使用单链表的尾插法插入数据
		LinkListTailInsert(h,time,str_lrc);
	}
}

//获取歌词
void GetLrc(const char* file_path) {
//以只读的方式打开文件
	FILE* fp =fopen(file_path,"r");
	//printf("%s",file_path);
	if (fp ==NULL) {
		perror("fopen");
		return;
	}

//将所有内存读取出来
//获取文件的总大小
	fseek(fp,0,SEEK_END);//读 头->end 
	int file_len = ftell(fp);
	rewind(fp);//指针移到文件开头 

//根据文件的总大小申请合适的空间
	file_data= (char*)malloc(file_len+1);
	if (file_data ==NULL) {
//内存不足时会导致空间开辟失败
		perror("malloc");
//关闭文件
		fclose(fp);
		return;
	}

//将所有文件内容读到内存空间当中
	fread(file_data,file_len,1,fp);

//将读出来的文件内容进行切割
	LrcDeal();

//遍历
	printf("file_data1111: %s\n",file_data);


//释放空间
	if (file_data ==NULL) {
		free(file_data);
		file_data =NULL;
	}


//关闭文件
	fclose(fp);
}




//创建一个空的单链表
LinkList *LinkListCreate() {
//第一步：在堆区开辟一个结点的空间
	LinkList* h=(LinkList*)malloc(sizeof(LinkList));

//第二步：让头结点的指针域标识为空
	h->next =NULL;

	printf("create successful\n");
	return h;
}

//遍历链表
void LinkListShow(LinkList* h) {
	if (h == NULL) {
		printf("链表不存在\n");
		return ;
	}
	LinkList* p = h;
//当p->next = NULL代表链表已经遍历到最后一个
	while(p->next != NULL) {
		p= p->next;

		printf("%d-%s\n",p->data.time,p->data.lrc);
	}
	//printf("*** 链表数据已经打印完成 ***\n");
}

//尾插法插入数据
void LinkListTailInsert(LinkList* h,int time,char* lrc) {
//第一步：将要插入的数据申请空间
	LinkList* temp = (LinkList*)malloc(sizeof(LinkList));

//第二步：给数据域赋值
	temp->data.time = time;
//注意：如果数据域当中有char*或者字符数组需要使用strcpy进行赋值
	strcpy(temp->data.lrc,lrc);

//第三步：将插入的数据中的指针域指向空
	temp->next =NULL;

//第四步：遍历找到最后一个节点
	LinkList* p = h;
	while(p->next !=NULL) {
		p = p->next;
	}

//第五步：将新插入的节点的地址保存在最后一个节点的指针域中
	p->next = temp;

//第六步：让最后的一个节点指针域指向空
	temp->next =NULL;

	//printf("*insert successful\n");


}

//根据时间去寻找歌词
LinkList* FindLrcByTime(LinkList* h,int time) {
//第一步：声明一个节点类型的指针来遍历
	LinkList* p = h;

//第二步：通过指针遍历链表找数据
	while(p->next != NULL) {
		if (p->data.time == time) {
//将对应的数据进行修改
			return p;
		}
		p = p->next;
	}

	return NULL;
}

//链表的注销(堆区申请的空间需要手动进行释放)
LinkList* FreeLinkList(LinkList* h) {
//第一步：判断是否已经释放
	if (h == NULL) {
		printf("链表不存在或者已释放\n");
		return h;
	}
//链表存在时,需要逐个进行释放
	else {
		LinkList* p = h;
//逐个节点进行释放
		while(p !=NULL) {
//让头结点保存下一个结点位置
			h = p->next;
//释放p的空间
			free(p);
//再让p指向h的地址
			p = h;
		}
		printf("链表已经释放\n");

	}
	return h;
}


